﻿using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Quartz;
using System.Threading.Tasks;
using Quartz.Impl.Matchers;
using Prism.Commands;
using System.Threading;
using System.Collections.ObjectModel;
using LiveCharts;

namespace ZYing.QPanel.ViewModels
{
    public class SchedulerViewModel : NavigationViewModelBase
    {
        private bool _support_IsJobGroupPaused = true;
        private bool _support_IsTriggerGroupPaused = true;
        public IScheduler Scheduler { get; }
        public SchedulerViewModel(IScheduler scheduler)
        {
            Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
            this.StartCommand = new DelegateCommand(this.Start);
            this.StandbyCommand = new DelegateCommand(this.Standby);
            this.ShutdownCommand = new DelegateCommand(this.Shutdown);
            this.PauseAllCommand = new DelegateCommand(this.PauseAll);
            this.ResumeAllCommand = new DelegateCommand(this.ResumeAll);
            Run(this.CancellationTokenSource.Token);
        }

        private SchedulerMetaData _metaData;

        public SchedulerMetaData MetaData
        {
            get { return _metaData; }
            set { SetProperty(ref _metaData, value); }
        }

        private static FilterInput Filter { get; } = new FilterInput { Limit = 40 };
        public async Task Reload()
        {
            this.MetaData = await this.Scheduler.GetMetaData();
            var executing = await Scheduler.GetCurrentlyExecutingJobs();
            JobsExecuting = executing.Count;
            var jobKeys = await Scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup());
            var triggerKeys = await Scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup());
            TotalJobs = jobKeys.Count;
            TotalTriggers = triggerKeys.Count;
            if (Scheduler.Context.TryGetExecutionHistoryStore(out var store))
            {
                this.JobsExecuted = await store.GetTotalJobsExecuted();
                this.JobsFailed = await store.GetTotalJobsFailed();
                var dic = this.History.ToDictionary(i => i.FireInstanceId, i => i);
                var logs = await store.FilterLast(Filter);

                foreach (var kv in logs.Reverse())
                {
                    if (dic.TryGetValue(kv.FireInstanceId, out var value))
                    {
                        var idx = this.History.IndexOf(value);
                        if (idx != -1)
                        {
                            this.History[idx] = value;
                        }
                        dic.Remove(kv.FireInstanceId);
                    }
                    else
                    {
                        this.History.Add(kv);
                    }
                }
                if (dic.Count > 0)
                {
                    foreach (var item in dic.Values)
                    {
                        this.History.Remove(item);
                    }
                }
            }
            else
            {
                this.JobsExecuted = this.JobsFailed = 0;
                this.History.Clear();
            }
            var jobGroups = await this.Scheduler.GetJobGroupNames();
            var dicJobGroups = this.JobGroups.ToDictionary(i => i.Group, i => i);
            foreach (var group in jobGroups)
            {
                if (dicJobGroups.TryGetValue(group, out var item))
                {
                    dicJobGroups.Remove(group);
                }
                else
                {
                    item = new JobGroupViewModel(Scheduler, group);
                    this.JobGroups.Add(item);
                }
                if (_support_IsJobGroupPaused)
                {
                    try
                    {
                        item.IsPaused = await this.Scheduler.IsJobGroupPaused(group);
                    }
                    catch (NotImplementedException)
                    {
                        _support_IsJobGroupPaused = false;
                        item.IsPaused = null;
                    }
                }
            }
            var triggerGroups = await this.Scheduler.GetTriggerGroupNames();
            var dicTriggerGroups = this.TriggerGroups.ToDictionary(i => i.Group, i => i);
            foreach (var group in triggerGroups)
            {
                if (dicTriggerGroups.TryGetValue(group, out var item))
                {
                    dicTriggerGroups.Remove(group);
                }
                else
                {
                    item = new TriggerGroupViewModel(Scheduler, group);
                    this.TriggerGroups.Add(item);
                }
                if (_support_IsTriggerGroupPaused)
                {
                    try
                    {
                        item.IsPaused = await this.Scheduler.IsTriggerGroupPaused(group);
                    }
                    catch (NotImplementedException)
                    {
                        _support_IsTriggerGroupPaused = false;
                        item.IsPaused = null;
                    }
                }
            }
        }
        private async void Run(CancellationToken cancellationToken)
        {
            while (true)
            {
                if (cancellationToken.IsCancellationRequested) break;
                if (this.IsActive)
                {
                    try
                    {
                        await Reload();
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine(e);
                    }
                }
                await Task.Delay(1000);
            }
        }

        private int _jobsExecuted;

        public int JobsExecuted
        {
            get { return _jobsExecuted; }
            set { SetProperty(ref _jobsExecuted, value); }
        }
        private int _jobsFailed;

        public int JobsFailed
        {
            get { return _jobsFailed; }
            set { SetProperty(ref _jobsFailed, value); }
        }
        private int _jobsExecuting;

        public int JobsExecuting
        {
            get { return _jobsExecuting; }
            set { SetProperty(ref _jobsExecuting, value); }
        }
        private int _totalJobs;

        public int TotalJobs
        {
            get { return _totalJobs; }
            set { SetProperty(ref _totalJobs, value); }
        }

        private int _totalTriggers;

        public int TotalTriggers
        {
            get { return _totalTriggers; }
            set { SetProperty(ref _totalTriggers, value); }
        }
        public string MachineName { get; } = Environment.MachineName;
        public string Application { get; } = Environment.CommandLine;

        public ObservableCollection<JobGroupViewModel> JobGroups { get; } = new ObservableCollection<JobGroupViewModel>();
        public ObservableCollection<TriggerGroupViewModel> TriggerGroups { get; } = new ObservableCollection<TriggerGroupViewModel>();

        public DelegateCommand StartCommand { get; }
        public async void Start()
        {
            await this.Scheduler.Start();
            await this.Reload();
        }
        public DelegateCommand StandbyCommand { get; }
        public async void Standby()
        {
            await this.Scheduler.Standby();
            await this.Reload();
        }
        public DelegateCommand ShutdownCommand { get; }
        public async void Shutdown()
        {
            if (!this.Scheduler.IsShutdown) await this.Scheduler.Shutdown(true);
            System.Windows.Application.Current.Shutdown();
        }
        public DelegateCommand PauseAllCommand { get; }
        public async void PauseAll()
        {
            await this.Scheduler.PauseAll();
            await this.Reload();
        }
        public DelegateCommand ResumeAllCommand { get; }

        public async void ResumeAll()
        {
            await this.Scheduler.ResumeAll();
            await this.Reload();
        }
        public ChartValues<ExecutionHistoryEntry> History { get; } = new ChartValues<ExecutionHistoryEntry>();
    }
}
